package com.ttg.web.filter;

import com.ttg.common.config.GatewayConfig;
import com.ttg.common.constant.BasicConstant;
import com.ttg.common.dto.BasicDto;
import com.ttg.common.dto.OrganEncryptDto;
import com.ttg.common.entities.BaseResult;
import com.ttg.common.entities.UserTokenInfo;
import com.ttg.common.enums.ErrorCodeEnums;
import com.ttg.common.exception.BaseException;
import com.ttg.common.utils.JsonUtils;
import com.ttg.common.utils.UserUtil;
import com.ttg.common.vo.OrganCheckSignVo;
import com.ttg.common.vo.OrganEncryptVo;
import com.ttg.web.config.CustomHttpServletRequestWrapper;
import com.ttg.web.config.CustomHttpServletResponseWrapper;
import com.ttg.web.manage.TransFormManager;
import com.ttg.web.service.ManageAuthService;
import com.ttg.web.service.OpenAuthService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.MimeHeaders;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.MediaType;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author zhangHongYuan
 * @description
 * @date 2023/7/5 17:20
 */
@Slf4j
//@Component
public class AuthFilter implements Filter {

    @Autowired
    private OpenAuthService openAuthService;

    @Autowired
    private ManageAuthService manageAuthService;
    @Autowired
    @Lazy
    private GatewayConfig gatewayConfig;

    @Value("${access.path}")
    private String accessPath;

    @Value("${tlinx.filter.excluded.urls:}")
    private String excludedUrls;
    private List<String> excludedPatterns ;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)  {
        try {
            System.out.println("----------------------------filter-----------------------------");

            HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
            // 检查是否应该排除
            if (shouldExclude(httpRequest.getRequestURI())) {
                // 直接放过，不执行过滤逻辑
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            }

            HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
            CustomHttpServletRequestWrapper requestWrapper = new CustomHttpServletRequestWrapper(httpRequest);
            String tokenType = requestWrapper.getHeader(BasicConstant.REQUEST_TOKEN_TYPE);
            String token = requestWrapper.getHeader(BasicConstant.TOKEN_REQUEST_TYPE);
            String openId = requestWrapper.getHeader(BasicConstant.OPENID_REQUEST_TYPE);
            String requestURI = requestWrapper.getRequestURI();
//        if (StringUtils.isBlank(tokenType)){
//            throw new BaseException(ErrorCodeEnums.REQUEST_ILLEGAL);
//        }

            log.info("请求参数Body:"+requestWrapper.getBody().toString());
            OrganCheckSignVo organCheckSignVo = new OrganCheckSignVo();
            if (!manageAuthService.ignoreAuthentication(requestURI.replaceFirst(accessPath,""))) {
                String body = requestWrapper.getBody();
                if (!JsonUtils.isJson(body) || StringUtils.isBlank(body)) {
                    throw new BaseException(ErrorCodeEnums.REQUEST_ILLEGAL);
                }
                BasicDto dto = JsonUtils.jsonToObject(body, BasicDto.class);
                dto.setToken(token);
                dto.setOpen_id(openId);
                String signType = requestWrapper.getHeader("sign_type");
                if (null == tokenType && StringUtils.isBlank(openId)) {
                    tokenType = BasicConstant.ORGAN_TYPE;
                }

                if (StringUtils.isNotBlank(tokenType)) {
                    String ipAddr = getIpAddr(requestWrapper);
                    Long chlId = getChlIdFromUrl(requestWrapper);
                    String tlinxAddress = requestWrapper.getHeader(BasicConstant.TLINX_ADDRESS);
                    dto.setUrl(requestURI);
                    dto.setTokenType(tokenType);
                    dto.setSign_type(signType);
                    decryptData(organCheckSignVo, dto);
                    organCheckSignVo.setIp(ipAddr);
                    organCheckSignVo.setChlId(chlId);
                    organCheckSignVo.setAddress(tlinxAddress);
                    organCheckSignVo.setSign_type(signType);
                    UserTokenInfo userTokenInfo = TransFormManager.transForUserInfo(organCheckSignVo);
                    if (Objects.nonNull(userTokenInfo)) {
                        UserUtil.setUser(userTokenInfo);
                        if (StringUtils.isNotBlank(organCheckSignVo.getData())) {
                            requestWrapper.setBody(organCheckSignVo.getData());
                        }
                    }
                } else if (StringUtils.isNotBlank(openId)) {
                    boolean b = openAuthService.verifyRsaSign(dto);
                    if (!b) {
                        throw new BaseException(ErrorCodeEnums.SIGN_ERROR);
                    }
                    String res = openAuthService.decryptAes(dto.getData());
                    requestWrapper.setBody(res);
                } else {
                    throw new BaseException(ErrorCodeEnums.REQUEST_ILLEGAL);
                }
            }
//        if (BasicConstant.ORGAN_TYPE.equals(tokenType)){
//
//        }else if (BasicConstant.OPEN_TYPE.equals(tokenType)){
//            OrganCheckSignVo organCheckSignVo = openAuthService.verifyRsaSign(dto);
//            if (ObjectUtils.isEmpty(organCheckSignVo)){
//                throw new BaseException(ErrorCodeEnums.SIGN_ERROR);
//            }
//            //String res = openAuthService.decryptAes(dto.getData());
//            requestWrapper.setBody(organCheckSignVo.getData());
//        }else {
//            throw new BaseException(ErrorCodeEnums.REQUEST_ILLEGAL);
//        }


            CustomHttpServletResponseWrapper responseWrapper = new CustomHttpServletResponseWrapper(httpResponse);
            System.out.println("----------------------------filterResponse-----------------------------");
            filterChain.doFilter(requestWrapper, responseWrapper);
            String contentType = responseWrapper.getContentType();
            byte[] content = responseWrapper.getResponseData();
            if (!(StringUtils.isNotBlank(contentType) && (contentType.contains("application/vnd.ms-excel")))){
                System.out.println("----元字符----"+new String(content,StandardCharsets.UTF_8));
            }
            String newStr ="";
            if (manageAuthService.ignoreEncryptUrl(requestURI.replaceFirst(accessPath,""))){

            }else if (StringUtils.isNotBlank(openId)){
                newStr = openAuthService.getAesDataAndRsaSign(new String(content,StandardCharsets.UTF_8));
            }else if (BasicConstant.ORGAN_TYPE.equals(tokenType)){
                newStr = responseDecorator(responseWrapper, organCheckSignVo, requestURI);
            }
            else if ("Urls".equals(requestURI)){

            }
            if (StringUtils.isNotBlank(contentType) && (contentType.contains(MediaType.APPLICATION_JSON_VALUE) || contentType.contains(MediaType.TEXT_HTML_VALUE))) {

                HttpServletResponse response=(HttpServletResponse)servletResponse;
                writeResponse(response,200,newStr);
            }else if (StringUtils.isNotBlank(contentType) && (contentType.contains("application/vnd.ms-excel"))) {

                HttpServletResponse response=(HttpServletResponse)servletResponse;
                try {
                    response.reset();
                    response.setHeader("Access-Control-Allow-Origin", "*");
                    response.setHeader("Access-Control-Allow-Methods", "*");
                    response.setContentType("application/vnd.ms-excel;charset=UTF-8");
                    response.setStatus(200);
                    response.getOutputStream().write(content);
                } catch (IOException e) {
                    throw new BaseException(ErrorCodeEnums.RESPONSE_ERROR);
                }

            }else if (manageAuthService.ignoreEncryptUrl(requestURI.replaceFirst(accessPath,""))){
                HttpServletResponse response=(HttpServletResponse)servletResponse;
                writeResponse(response,200,new String(content,StandardCharsets.UTF_8));
            }
        }catch (BaseException baseException){
            log.error("AuthFilter ====>",baseException);
            dellFilterException(baseException.getErrcode(),baseException.getMsg(),servletResponse);
        }catch (Exception e){
            log.error("AuthFilter ====>",e);
            dellFilterException(ErrorCodeEnums.SYSTEM_ERROR.getErrcode(),ErrorCodeEnums.SIGN_ERROR.getMsg(),servletResponse);
        }
    }

    public static void writeResponse(HttpServletResponse response, int status, String json) {
        try {
            response.reset();
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Methods", "*");
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(status);
            response.getWriter().write(json);
        } catch (IOException e) {
            throw new BaseException(ErrorCodeEnums.RESPONSE_ERROR);
        }
    }
    public  String getBodyString(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }

    private void buildBaseDto(BasicDto getBaseDto, BasicDto baseDto) {
        if (StringUtils.isNotBlank(getBaseDto.getLang())) {
            baseDto.setLang(getBaseDto.getLang());
        }
        if (StringUtils.isNotBlank(getBaseDto.getToken())) {
            baseDto.setToken(getBaseDto.getToken());
        }
        if (StringUtils.isNotBlank(getBaseDto.getRandStr())) {
            baseDto.setRandStr(getBaseDto.getRandStr());
        }
        if (StringUtils.isNotBlank(getBaseDto.getTimestamp())) {
            baseDto.setTimestamp(getBaseDto.getTimestamp());
        }
        if (StringUtils.isNotBlank(getBaseDto.getOpen_id())) {
            baseDto.setOpen_id(getBaseDto.getOpen_id());
        }
        if (StringUtils.isNotBlank(getBaseDto.getTokenType())) {
            baseDto.setTokenType(getBaseDto.getTokenType());
        }
        if (StringUtils.isNotBlank(getBaseDto.getData())){
            baseDto.setData(getBaseDto.getData());
        }
        if (StringUtils.isNotBlank(getBaseDto.getSign())){
            baseDto.setSign(getBaseDto.getSign());
        }
    }

    private void decryptData(OrganCheckSignVo checkSignVo, BasicDto baseDto) {
        //调用鉴权服务、解密用户上送的数据
        OrganCheckSignVo temp = manageAuthService.checkSign(baseDto);
        BeanUtils.copyProperties(temp, checkSignVo);
        checkSignVo.setLang(baseDto.getLang());
    }

    public static String getIpAddr(HttpServletRequest requestWrapper) {



        //X-Forwarded-For：Squid Proxy Server
        String ipAddress = requestWrapper.getHeader("X-Forwarded-For");

        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            //Proxy-Client-IP：Apache Proxy Server
            ipAddress = requestWrapper.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            //WL-Proxy-Client-IP：Weblogic Proxy Server
            ipAddress = requestWrapper.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            //X-Real-IP：Nginx Proxy Server
            ipAddress = requestWrapper.getHeader("X-Real-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = requestWrapper.getRemoteHost();
            if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
                // 根据网卡取本机配置的IP
                try {
                    InetAddress inet = InetAddress.getLocalHost();
                    ipAddress = inet.getHostAddress();
                } catch (Exception e) {
                    log.error("根据网卡获取本机配置的IP异常", e);
                }
            }
        }
        // 对于通过多个代理的情况，第一个IP为客户端真实IP，多个IP按照','分割
        if (ipAddress != null && ipAddress.indexOf(",") > 0) {
            ipAddress = ipAddress.split(",")[0];
        }
        return ipAddress;
    }

    private Long getChlIdFromUrl(HttpServletRequest requestWrapper) {
        String chlIdStr = requestWrapper.getHeader(BasicConstant.TLINX_CHL_ID);

        return StringUtils.isNotBlank(chlIdStr) ? Long.valueOf(chlIdStr) : null;
    }



    public static Map<String, String> getUrlQueryMap(HttpServletRequest request) {
        String urlQueryString = request.getQueryString();
        Map<String, String> queryMap = new HashMap<>();
        String[] arrSplit;
        if (urlQueryString == null) {
            return queryMap;
        } else {
            //每个键值为一组
            arrSplit = urlQueryString.split("[&]");
            for (String strSplit : arrSplit) {
                String[] arrSplitEqual = strSplit.split("[=]");
                //解析出键值
                if (arrSplitEqual.length > 1) {
                    queryMap.put(arrSplitEqual[0], arrSplitEqual[1]);
                } else {
                    if (!"".equals(arrSplitEqual[0])) {
                        queryMap.put(arrSplitEqual[0], "");
                    }
                }
            }
        }
        return queryMap;
    }


    private void reflectSetHeader(CustomHttpServletRequestWrapper requestWrapper, String key, String value){
        Class<? extends HttpServletRequest> requestClass = requestWrapper.getClass();
        try {
            Field requestField = requestClass.getDeclaredField("request");
            requestField.setAccessible(true);
            Object requestObj = requestField.get(requestWrapper);
            Field coyoteRequestField = requestObj.getClass().getDeclaredField("coyoteRequest");
            coyoteRequestField.setAccessible(true);
            Object coyoteRequestObj = coyoteRequestField.get(requestObj);
            Field headersField = coyoteRequestObj.getClass().getDeclaredField("headers");
            headersField.setAccessible(true);
            MimeHeaders headersObj = (MimeHeaders)headersField.get(coyoteRequestObj);
            headersObj.removeHeader(key);
            headersObj.addValue(key).setString(value);
        } catch (Exception e) {
            log.error("reflect set header {} error {}", key, e);
        }
    }

    private String responseDecorator(CustomHttpServletResponseWrapper responseWrapper, OrganCheckSignVo tempVo,String requestURI) throws IOException {
        String contentType = responseWrapper.getContentType();
        byte[] content = responseWrapper.getResponseData();
        // 返回的数据
        String rootData = new String(content, StandardCharsets.UTF_8);
        log.info("上游服务返回的数据" + rootData);
        byte[] respData;
        try {
            BaseResult result = JsonUtils.jsonToObject(rootData, BaseResult.class);
            boolean isSuccess = false;
            if (ErrorCodeEnums.SUCCESS.getErrcode().equals(result.getErrcode())) {
                if (null != result.getData()) {
                    OrganEncryptDto encryptDataDto = new OrganEncryptDto();
                    encryptDataDto.setOpenId(tempVo.getOpenId());
                    encryptDataDto.setRequestType(tempVo.getRequestType());
                    encryptDataDto.setData(JsonUtils.objectToJson(result.getData()));
                    encryptDataDto.setOrgPrivateKey(tempVo.getOrgPrivateKey());
                    encryptDataDto.setAesKey(tempVo.getAesKey());
                    encryptDataDto.setUrl(tempVo.getUrl());
                    encryptDataDto.setSign_type(tempVo.getSign_type());
                    //响应数据处理
                    OrganEncryptVo requestReturnVo = manageAuthService.encryptData(encryptDataDto);
                    result.setData(requestReturnVo.getData());
                    result.setSign(requestReturnVo.getSign());
                    result.setErrcode(requestReturnVo.getErrcode());
                    result.setMsg(requestReturnVo.getMsg());
                    log.info("加密后的数据" + result);
                    isSuccess = true;
                }
            }
            respData = JsonUtils.objectToJson(result).getBytes();
        } catch (BaseException e) {
            log.error("系统异常: ", e);
            throw e;
        } catch (Exception e) {
            log.error("GlobalResponselFilter--数据响应处理异常", e);
            throw new BaseException(ErrorCodeEnums.SYSTEM_ERROR);
        }
        // 加密后的数据返回给客户端
       return new String(respData, StandardCharsets.UTF_8);
    }


    private String encodeBody(Map<String, String> map) {
        return map.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining("&"));
    }



    private void dellFilterException(String errorCode,String msg,ServletResponse servletResponse){
        OrganEncryptVo result = new OrganEncryptVo();
        result.setErrcode(errorCode);
        result.setMsg(msg);
        byte[] respData = JsonUtils.objectToJson(result).getBytes();
        String respString = new String(respData, StandardCharsets.UTF_8);
        HttpServletResponse response=(HttpServletResponse)servletResponse;
        writeResponse(response,200,respString);
    }

    private boolean shouldExclude(String uri) {
        if(excludedPatterns==null){
            String patterns = excludedUrls;
            if (patterns != null) {
                excludedPatterns = Arrays.asList(patterns.split("\\s*,\\s*"));
            } else {
                excludedPatterns = Collections.emptyList();
            }
        }

        for (String pattern : excludedPatterns) {
            if (uri.startsWith(pattern.replace("*", ""))) {
                return true;
            }
            // 或者使用更复杂的模式匹配
            if (Pattern.matches(pattern.replace("*", ".*"), uri)) {
                return true;
            }
        }
        return false;
    }
}
